#!/usr/bin/env python
import time
import struct
import os
import socket
import network
import ntptime
from machine import WDT
from machine import Pin
from machine import SPI
from machine import UART
from machine import Timer
import machine
import btree
import dht

IP="101.201.239.239"
PORT=502
ESSID="189"
PASSWORD="18910158363"

#connect  WIFI
def do_connect(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('connecting to network...')
        wlan.connect(ssid, password)
        while not wlan.isconnected():
            pass
    print('network config:', wlan.ifconfig())
    ntptime.host = "ntp.ntsc.ac.cn"
    try:
       ntptime.settime()
    except:
        pass
    try:
       ntptime.settime()
    except:
        pass

class BLIK_LED:
    def __init__(self, num, value=0):
        self.pin = Pin(num, Pin.OUT, value=0)
        self.tim = Timer(-1)
        self.value = value
        self.pin.value(value)
    def __toggle(self, t):
        self.pin.value(self.value & 0x01)
        self.value += 1
    def start(self):
        self.tim.init(period=500, mode=Timer.PERIODIC, callback=self.__toggle)
    def stop(self):
        self.tim.deinit()
        self.pin.value(1)

class UINPUT:
    def __init__(self):
        #u2rxd->16 u2txd->17
        self.uart = UART(2, baudrate=115200)

    def recv(self):
        data = self.uart.read()
        offset = 0
        out = dict()
        if data is None:
            return out

        while offset < len(data):
            if data[offset] == ord('#'):
                offset += 1
                code = data[offset: offset+1]
                offset += 1
                length = int(data[offset: offset+2])
                offset += 2
                para = data[offset: offset+length]
                offset += length
                out[code] = para
            else:
                offset += 1
        return out

    def send(self, buf=None):
        return self.uart.write(buf)

class MAX485:
    def __init__(self):
        #u1rxd->15 u1txd->2, rd/wt = pin4
        self.uart = UART(1, baudrate=115200, rx=15,tx=2)
        self.rdwt = Pin(4, Pin.OUT, value=0)

    def recv(self):
        self.rdwt.value(0)
        return self.uart.read()

    def send(self, buf=None):
        self.rdwt.value(1)
        return self.uart.write(buf)

class AD7606:
    #x/5 X 300
    CUR_FACTOR = 60
    VOL_FACTOR = 20
    SSSD_FACTOR = 60
    AIR_FACTOR = 60
    HANJIAN_FACTOR = 60
    def __init__(self):
        #sclk stay 1 when idle
        self.spi = SPI(1, baudrate=1000000, polarity=1, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
        self.cs = Pin(27, Pin.OUT, value=1)
        self.convab = Pin(26, Pin.OUT, value=1)
        self.busy = Pin(25, Pin.IN, Pin.PULL_UP)
        self.reset = Pin(33, Pin.OUT, value=0)
        time.sleep_us(1)

    def __reset(self):
        self.reset.value(1)
        time.sleep_us(2)
        self.reset.value(0)
        time.sleep_us(2)

    def __realval(self, data):
        offset = 0

        cur = struct.unpack('>h', data[offset: offset+2])[0] / 3276.8
        rcur = cur * AD7606.CUR_FACTOR
        offset += 2

        vol = struct.unpack('>h', data[offset: offset+2])[0] / 3276.8
        rvol = vol * AD7606.VOL_FACTOR
        offset += 2

        air = struct.unpack('>h', data[offset: offset+2])[0] / 3276.8
        rair = air * AD7606.AIR_FACTOR
        offset += 2

        hanjian_temp = struct.unpack('>h', data[offset: offset+2])[0] / 3276.8
        temperature = hanjian_temp * AD7606.HANJIAN_FACTOR
        offset += 2
        #sssd = current/2.3
        return rcur, rvol, rcur/2.3, rair, temperature

    def sample(self):
        self.__reset()
        # 16bit/per channel  8 channells
        dout = bytearray(16)
        self.convab.value(0)
        time.sleep_us(2)
        self.convab.value(1)
        while True:
            time.sleep_us(2)
            busy = self.busy.value()
            if busy == 0:
                break

        self.cs.value(0)
        time.sleep_us(2)
        for j in range(8):
            temp = self.spi.read(2)
            dout[(j * 2): (j * (2+1))] = temp

        self.cs.value(1)
        return self.__realval(dout)

class ModBus:
    def __init__(self, slave=0x01):
        self.tid = 0x0001
        self.protocol = 0x0000
        self.len = 0x0069
        self.slave = slave      #uint_8
        self.function = 0x10    #uint_8
        self.reg_addr = 0x0000;
        self.reg_num  = 0x0031;
        self.reg_len  = 0x62;   #uint_8
        self.time =    bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) #12 bytes
        self.device =  bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])        #10 bytes
        self.gongyi  = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])       #10 bytes
        self.hangong = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])        #10 bytes
        self.gongwei = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])       #20 bytes
        self.current = 0.0
        self.voltage = 0.0
        self.sssd = 0.0
        self.airflu = 0.0
        #to be modified
        self.temperature = 0.0
        self.humidity = 0.0
        self.hanjian_temp = 0.0
        #self.speed = 0.0
        #self.hqbf = 0.0
        #self.hqbs = 0.0
        #self.hjcd = 0.0
        #self.hjjd = 0.0

    def set_device(self, device=None, gongyi=None, hangong=None, gongwei=None):
        l = len(device)
        i = 0
        while i < l - 1:
            self.device[i] = device[i+1]
            self.device[i+1] = device[i]
            i += 2 

        l = len(gongyi)
        i = 0
        while i < l - 1:
            self.gongyi[i] = gongyi[i+1]
            self.gongyi[i+1] = gongyi[i]
            i += 2 


        l = len(hangong)
        i = 0
        while i < l - 1:
            self.hangong[i] = hangong[i+1]
            self.hangong[i+1] = hangong[i]
            i += 2 

        l = len(gongwei)
        i = 0
        while i < l - 1:
            self.gongwei[i] = gongwei[i+1]
            self.gongwei[i+1] = gongwei[i]
            i += 2 

    def set_current(self, current, voltage, sssd, airflu, hj_temp):
        self.current = current
        self.voltage = voltage
        self.sssd = sssd
        self.airflu = airflu
        self.hanjian_temp = hj_temp

    def set_temp_humid(self, temperature, humidity):
        self.temperature = temperature
        self.humidity = humidity

    def __set_time(self):
        now = time.localtime()
        nowstr = "{:0>2d}{:0>2d}{:0>2d}{:0>2d}{:0>2d}{:0>2d}".format((now[0] - 2000), now[1], now[2], now[3], now[4], now[5])
        print (nowstr)
        for x in range(6):
            self.time[2*x] = ord(nowstr[2*x+1])
            self.time[2*x + 1] = ord(nowstr[2*x])

    def gen_bytes(self):
        offset = 0
        out = bytearray(111)
        self.__set_time()

        struct.pack_into(">H", out, offset, self.tid)
        offset += 2

        struct.pack_into(">H", out, offset, self.protocol)
        offset += 2

        struct.pack_into(">H", out, offset, self.len)
        offset += 2

        struct.pack_into(">B", out, offset, self.slave)
        offset += 1

        struct.pack_into(">B", out, offset, self.function)
        offset += 1

        struct.pack_into(">H", out, offset, self.reg_addr)
        offset += 2

        struct.pack_into(">H", out, offset, self.reg_num)
        offset += 2

        struct.pack_into(">B", out, offset, self.reg_len)
        offset += 1
        ##########
        
        out[offset: offset+12] = self.time
        offset += 12
        
        out[offset: offset+10]  = self.device 
        offset += 10

        out[offset: offset+10] = self.gongyi
        offset += 10

        out[offset: offset+10] = self.hangong
        offset += 10

        out[offset: offset+20] = self.gongwei
        offset += 20

        currentbytes = struct.pack("<f", self.current)
        out[offset: offset+2] = currentbytes[0:2]
        out[offset+2: offset+3] = currentbytes[3:4]  #shit 
        out[offset+3: offset+4] = currentbytes[2:3]  #shit
        offset += 4
       
        voltagebytes = struct.pack("<f", self.voltage)
        out[offset: offset+2] = voltagebytes[0:2]
        out[offset+2: offset+3] = voltagebytes[3:4]
        out[offset+3: offset+4] = voltagebytes[2:3]
        offset += 4

        sssdbytes = struct.pack("<f", self.sssd)
        out[offset: offset+2] = sssdbytes[0:2]
        out[offset+2: offset+3] = sssdbytes[3:4]
        out[offset+3: offset+4] = sssdbytes[2:3]
        offset += 4

        airflubytes = struct.pack("<f", self.airflu)
        out[offset: offset+2] = airflubytes[0:2]
        out[offset+2: offset+3] = airflubytes[3:4]
        out[offset+3: offset+4] = airflubytes[2:3]
        offset += 4
        ####to be realized

        temperaturebytes = struct.pack("<f", self.temperature)
        out[offset: offset+2] = temperaturebytes[0:2]
        out[offset+2: offset+3] = temperaturebytes[3:4]
        out[offset+3: offset+4] = temperaturebytes[2:3]
        offset += 4

        humiditybytes = struct.pack("<f", self.humidity)
        out[offset: offset+2] = humiditybytes[0:2]
        out[offset+2: offset+3] = humiditybytes[3:4]
        out[offset+3: offset+4] = humiditybytes[2:3]
        offset += 4

        hanjian_tempbytes = struct.pack("<f", self.hanjian_temp)
        out[offset: offset+2] = hanjian_tempbytes[0:2]
        out[offset+2: offset+3] = hanjian_tempbytes[3:4]
        out[offset+3: offset+4] = hanjian_tempbytes[2:3]
        offset += 4
        
        self.tid += 1
        return out

class TEP_HID:
    def __init__(self, pin=19):
        self.d = dht.DHT22(machine.Pin(pin))

    def get_sample(self):
        try:
            self.d.measure()
        except OSError:
            #timeout, return default value
            return 26.0, 45.0

        t = self.d.temperature() # temperature. 23.6 (°C)
        h = self.d.humidity()    # humidity 41.3 (% RH)
        return round(t, 1), round(h, 1)

def offset_get():
    try:
        with open("offset", "r") as fs:
            data = fs.read()
            return int(data)
    except:
        return 0

def offset_set(offset):
    if offset < 0:
        offset = 0
    with open("offset", "w") as fs:
        fs.write(str(offset))
    
def get_size():
    try:
        return os.stat("data")[6]
    except:
        return 0

def start():
    wdt = WDT(timeout=60000)
    scan_gun = BLIK_LED(23, value=1)
    wifi_led = BLIK_LED(22)
    alarm_led = BLIK_LED(21)
    temp_hum = TEP_HID(19)
    parameters = dict()

    reset_reason = machine.reset_cause()
    if reset_reason == machine.SOFT_RESET or reset_reason == machine.WDT_RESET:
        f = open("mydb", "r+b")
        db = btree.open(f)
        for k, v in db.items():
            parameters[k]  = v
        db.close()
        f.close()
        print ("loading parameters", parameters)

    if reset_reason == machine.PWRON_RESET:
        scan_gun.start()
        uinput = UINPUT()
        #uinput.send(b'#110DC400-0001#206FCAW-S#308JSYJ-001#419GDGW-T01-ZET02-JS01#703189#81118910158363#915101.201.239.239')
        while len(parameters) <= 0:
            parameters = uinput.recv()
            time.sleep(1)
        scan_gun.stop()
        f = open("mydb", "w+b")
        db = btree.open(f)
        for k, v in parameters.items():
            db[k] = v
        db.flush()
        db.close()
        f.close()
        print ("saving parameters", parameters)

    wifi_led.start()
    modbus =  ModBus()
    modbus.set_device(parameters[b"1"], parameters[b"2"],  parameters[b"3"], parameters[b"4"])

    ssid = parameters.get(b"7")
    if ssid is None:
        ssid = ESSID
    password = parameters.get(b"8")
    if password is None:
        password = PASSWORD
    do_connect(ssid, password)

    ad = AD7606()

    skt = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ipaddr = parameters.get(b"9")
    if ipaddr is None:
        ipaddr = IP
    skt.settimeout(2)
    netok = True
    try:
        skt.connect((ipaddr, PORT))
    except OSError:
        netok = False
    if netok:
        wifi_led.stop()
    skt.settimeout(1)
    size = get_size()
    offset = offset_get()
    while True:
        wdt.feed()
        start = time.ticks_ms()
        cul, vol, sssd, airflu, hanjian_t = ad.sample()
        modbus.set_current(cul, vol, sssd, airflu, hanjian_t)
        temperature, humidity = temp_hum.get_sample()
        modbus.set_temp_humid(temperature, humidity)
        out =  modbus.gen_bytes()
        if netok:
            try:
                skt.send(out)
                skt.recv(16)
            except OSError:
                netok = False
                wifi_led.start()
                if size < 2048000:
                    with open('data', 'ab') as fs:
                        fs.write(out)
                        size += 111
                continue
        else:
            wifi_led.start()
            if size < 2048000:
                with open('data', 'ab') as fs:
                    fs.write(out)
                    size += 111
            if size % 6660 == 0:
                wlan = network.WLAN(network.STA_IF)
                if wlan.isconnected():
                    machine.reset()
        #sendout saved data
        if netok:
            try:
                with open('data', 'rb') as fs:
                    fs.seek(offset)
                    while True:
                        item = fs.read(111)
                        if len(item) != 111:
                            break
                        try:
                            skt.send(item)
                            skt.recv(16)
                        except OSError:
                            offset = fs.tell() - 111
                            offset_set(offset)
                            machine.reset()
                    os.remove('data')
                    offset_set(0)
            except:
                pass

        delta = time.ticks_diff(time.ticks_ms(), start)
        nap = 1000 - delta 
        if nap > 0:
            time.sleep_ms(nap)
            print ("sleep ms", nap)

